js-12 拖拽、缓冲、懒加载

一、拖拽

  1. 事件拖拽;

利用offset值来计算

  1. 拖拽的问题:如果拖拽的地方有文字并且在拖拽 div 的时候,文字是被选中的状态,浏览器会认为是拖拽文字,或者如果要拖拽的元素是图片,浏览器会认为是保存图片效果,这都是因为浏览器的默认行为;
  2. 解决拖拽问题:

(1)普通浏览器可以利用return false,阻止浏览器默认行为;

(2)IE浏览器需要使用事件捕获来阻止浏览器默认行为,事件捕获在普通浏览器里面会出错,所以需要做兼容;

二、事件捕获(IE独有)

obj.setCapture();页面中和元素一样的事件都捕获起来,将页面中其他相同的事件触发时都执行元素的事件处理函数;让其他元素都执行调用捕获元素的条件;

  1. 释放捕获:obj.releaseCapture();取消继续捕获;每次点击都会捕获一次,所以抬起需要取消捕获;

三、限制盒子移动范围

拖拽不能超出屏幕,通过下图我们能得到:oDiv.left+oDiv.offsetWidth 不能大于屏幕宽。

鼠标按下时:

var x = ev.clientX - obj1.offsetLeft;
var y = ev.clientY - obj1.offsetTop;

鼠标移动时:

var l = ev.clientX - x;
var t = ev.clientY - y;

判断是否有碰撞:

if(l<0){l = 0;} // 大于 0 并且小于屏幕宽再回来自己的宽度
if(l>document.documentElement.clientWidth- obj1.offsetWidth){
    l = document.documentElement.clientWidth - obj1.offsetWidth;
}
if (t<0){ t = 0;} //  
if(t>document.documentElement.clientHeight - obj1.offsetHeight){
    t = document.documentElement.clientHeight-obj1.offsetHeight;
}

赋值给div;

obj1.style.top = t + “px”;
obj1.style.left = l + “px”;

当鼠标抬起时:

document.onmousemove = document.onmouseup = “”;
if(obj2.setCapture){
    obj2.releaseCapture();
}

image

四、碰撞检测;

中间使用||连接;

左:move.offsetLeft+move.offsetWidth < box.offsetLeft

上:move.offsetTop+move.offsetHeight < box.offsetTop

右:move.offsetLeft > box.offsetLeft+box.offsetWidth

下:move.offsetTop > box.offsetTop + box.offsetHeight

image

五、缓冲运动

公式(目标点 - 当前位置)/ 缩放系数

如果改变的样式是opacity需要乘以100

取整 如果大于0,则向上取整,如果小于0则向下取整。

根据运动是否完成来设定flag开关。

  1. 做小盒子缓冲移动 -----每次加的值也是一个变量;
  2. 封装
  3. 改变透明度 ----判断透明度的值 不能为小数,所以*100;
  4. 做对象 做多值缓冲运动
  5. 改变清除定时器的时间;
  6. 加一个回调函数,动画完成;

六、懒加载

先只加载可视窗口区域的图片,当用户向下拖动滚动条时再继续加载后面可视图片;

优点:

(1)这样减少了加载时的线程数量,使可视区域内的图片也能够快速加载,优化了用户体验。

(2)减少了同一时间发向服务器的请求数,服务器压力剧减。

  1. 在写网页<img>标签时,将图片的路径放到自定义属性_src路径中,在加载页面时,这个图片一开始是无法加载,当浏览器的可视区域移动到此图片上时,将_src中的路径赋值给src属性,并开始加载,即浏览器的可视距离加上浏览器被卷起的高度大于等于图片到浏览器顶部的距离;
$(window).scroll(function(e){
    $("img").each(function(){ //如果浏览器可视高度加上浏览器被卷起的高度 大于等于 图片到浏览器顶部的距离,则显示
        if($(window).height() + $(window).scrollTop() >= $(this).offset().top + 300){
            $(this).attr("src",$(this).attr("_src"));
        }
    })
})

七、自定义滚动条

  1. 设置滚动条条儿的高度

猜想:滚动条的高度随着内容的高度而变化,因此它们需要成正比;

结果:列表可见的高度 / 列表实际的高度 = x / 列表可见的高度

x = 列表可见的高度 / 列表实际的高度 * 列表可见的高度

function setBarHeight() {
    var scrollHeight = listDom.scrollHeight;
    var h = listClientHeight / scrollHeight * listClientHeight;
    if (h >= listClientHeight) {
        h = 0; //内容过少,滚动条高度超过了可见区域,不需要滚动条
    }
    barDom.style.height = h + "px";
}
  1. 设置滚动条的位置

猜想:滚动条移动的位置要与列表被卷起的高度成正比;滚动条的可区域相当于列表可见的高度;

结果:列表被卷起的高度 / 列表实际内容的高度 = x / 列表可见的高度;

x = 列表被卷起的高度 / 列表实际内容的高度 * 列表可见的高度;

function setBarTop() {
    var top = listDom.scrollTop / listDom.scrollHeight * listClientHeight;
    barDom.style.top = top + "px";
}
  1. 判断鼠标滚动事件

根据onmousewheel传入一个e,取值e.detailY,用列表被卷起的高度去相加

listDom.onmousewheel = function(e){
    setListScrollTop(listDom.scrollTop + e.detailY)   //调用设置列表被卷起的高度的高度;
}
  1. 设置列表的scrolltop
function setListScrollTop(newScrollTop) { 
   // 这里传入的值可能为负数,因为会向上或向下滚动;
 
    clearInterval(timer); //避免重复调用函数,停止之前的移动
    //使用动画实现滚动
    //例如:scrollTop  300 -> 100
    var spd = 0.8; //速度:1毫秒变化的距离(自定义的)
    var dis = newScrollTop - listDom.scrollTop; //移动的总距离
    var tick = 10; //多少毫秒移动一次
    var duration = Math.abs(dis / spd); //计算移动的总时间,因为可能是负数,所以需要取绝对值;
    var times = Math.ceil(duration / tick); //一共可以移动多少次
    var curTimes = 0; //当前移动了多少次
    var everyDis = dis / times; //每次移动的距离 = 总距离/总次数
    timer = setInterval(function () {
        curTimes++;
        listDom.scrollTop += everyDis;
        setBarTop();
        if (curTimes === times) {
            clearInterval(timer);
        }
    }, tick);
}
  1. 拖动滚动条条儿事件,根据滚动条 条儿位置,直接设置列表滚动的高度;

猜想:列表滚动被卷起的高度要 与 滚动条条儿与列表可见的高度的顶部距离成正比;

结果:条儿的位置 / 列表可见的高度 = x / 列表总高度 (成正比)x 为被卷起的高度;

function setListScrollTopFromBarTop(top) {   // 这个top需要被调用;
    var sh = top / listClientHeight * listDom.scrollHeight;
    listDom.scrollTop = sh;
}



barDom.onmousedown = function (e) {
    var y = e.pageY; //相对于网页的y坐标
    var top = parseFloat(getComputedStyle(this).top); //按下时的top值
    var h = barDom.clientHeight; //滚动条条儿自身的高度
    window.onmousemove = function (e) {
        var newY = e.pageY;
        var newTop = top + (newY - y);
        if (newTop < 0) {
            newTop = 0;
        }
        else if (newTop > listClientHeight - h) {
            newTop = listClientHeight - h;
        }
        barDom.style.top = newTop + "px";
        setListScrollTopFromBarTop(newTop);
    }
    window.onmouseup = function () {
        window.onmousemove = null;
    }
}

八、监听图片的error事件

  1. 当一个图片资源加载出错时,可以替换成默认的图片
<img src='' onerror='' alt='' />
  1. 全局监听:当一个页面过多图片时,可以使用全局监听,需要使用冒泡
window.addEventListener('error',()=>{
    if(e.target.)
},true)
  1. 解决无限触发网络出现异常
window.addEventListener('error',function(e){
    let times = Number(e.target.dataset.times) || 0, 以失败的次数,默认为0
        allTimes = 3; //总失败次数
    if(e.target.tagName.toUpperCase() === 'IMG'){
        if(times >= allTimes){
            e.target.src = '' 提供一个base64的默认图片
        } else {
            e.target.dataset.times = times++
            e.target.src = '//default.jpg'
        }
    }
},true)

高频面试题

● 拖拽效果中有几种事件?

● 什么是回调函数?